package calculate;

import java.util.ArrayList;

/**
 * used Bailey–Borwein–Plouffe formula to generate pi bit by bit
 * use multi-thread to get the reslt
 */
public class BitByBitMulti extends Basic implements Runnable {
	
	static long[] thePower;
	long start = System.nanoTime();
	long end = System.nanoTime();
	long mid = System.nanoTime();
	static final int numberOfThread = 15;
	int threadNumber;
	
	public BitByBitMulti(int k) {
		super(k);
		thePower = new long[k+100];
		java.util.Arrays.fill(thePower, -1);
	}
	
	public BitByBitMulti() {
	}

	@Override
	public void calculous() {
		System.out.println("program started at: " + this.start);
		ArrayList<Thread> threads = new ArrayList<Thread>();
		for (int i = 0; i < this.numberOfThread; ++ i) {
			BitByBitMulti th = new BitByBitMulti();
			th.threadNumber = i;
			Thread t = new Thread(th, "subThread : "+i);
			threads.add(t);
			t.start();
		} 
		for (int i = 0; i < this.numberOfThread; ++i ) {
			try {
				threads.get(0).join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
		}
		this.end = System.nanoTime();
		System.out.println("program finished at : " + this.end);
		System.out.println("program used: " + (this.end - this.start));
	}
	
	public void run() {
		int processed = 0;
		System.out.println("thread " + this.threadNumber + " start at: " + this.start);
		for (int i = -1; i < this.preciseDigit; ++i) {
			if ((i+1) % this.numberOfThread == this.threadNumber) {
				++processed;
				int temp = getBit(i);
				this.hex[i+1]=new Integer(temp);
				
				if (processed % 100 == 0) {
					mid = end;
					end = System.nanoTime();
					System.out.println("[TID="+this.threadNumber+"] number="+
							processed+", i=" + i + " \tpi=" + temp + "\t[t:"+(end-mid)+"(nanoTime)]" );				
				}				
			}
		}
		this.end = System.nanoTime();
		System.out.println("thread " + this.threadNumber + " stoped at: " + this.end);
		System.out.println("thread " + this.threadNumber + " used: " + (this.end-this.start));
	}

	private Integer getBit(int i) {

		double t  = 0;
		t += this.part(1, i) * 4.0;
		t -= this.part(4, i) * 2.0;
		t -= this.part(5, i);
		t -= this.part(6, i);
		while(t<0) t+=1;
		t %= 1.0;

		//System.out.println( "t=" +  t*16 );
		return this.getFirstDigit(t*16);
	}
	
	//calculous the small part as : (b * 16^k) / (8 * k + a)
	//calculous counts from 0 to k
	public double part( int a, int n ) {

		double r = 0;
		double oldR;
		long u = 0; 
		long d = 0;
		int i = 0;
		
		
		for (i=0; i<=n; ++i) {
			d = 8 * i + a;
			r += ((double)this.power( 16, n - i, d)) / (d);
			if (r > 100) r %= 1.0;
		}
		
		while(true) {
			oldR = r;
			d = 8 * i + a;
			r = r + 1.0/(this.power(16, i - n) * d);
			if (r - oldR < 0.1) break;
			++i;
		}
		return r;
	}
	
	//cal b^p
	public long power(int b, int p) {
		if (this.thePower[p] != -1) return this.thePower[p];
		if (p==0) {
			this.thePower[p] = 1;
		}
		else {
			long temp = power(b, p/2 );
			temp = temp * temp;
			if (p%2==1) temp *= b;
			this.thePower[p] = temp;
		}
		return(this.thePower[p]);
	}

	//cal (b^p) mod m
	public long power( long b, long p, long m) {	
		if (p==0) return 1;
		long temp = power(b, p/2, m);
		temp = temp * temp;
		if (p%2==1) temp *= b;
		temp %= m;
		return (temp);
	}
}
